package com.gitee.qdbp.base.orm.mybatis.interceptor;

import java.util.Collection;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.defaults.DefaultSqlSession;
import com.gitee.qdbp.base.controlling.ControllingTools;
import com.gitee.qdbp.base.controlling.IDataIsolationControlling;
import com.gitee.qdbp.base.enums.OperateType;

/**
 * 利用MyBatis插件机制实现数据隔离
 *
 * @author zhaohuihua
 * @version 170612
 */
@Intercepts({
        @Signature(type = Executor.class, method = "query",
            args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }),
        @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) })
public class DataIsolationInterceptor extends BaseExecutorInterceptor {

    private IDataIsolationControlling dataIsolationControlling;

    public DataIsolationInterceptor(IDataIsolationControlling dataIsolationControlling) {
        this.dataIsolationControlling = dataIsolationControlling;
    }

    /** 利用MyBatis插件机制拦截待处理对象 **/
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object[] args = invocation.getArgs();

        MappedStatement statement = (MappedStatement) args[0];
        SqlCommandType cmd = statement.getSqlCommandType();
        OperateType operateType = ControllingTools.convertOperateType(cmd);

        for (Object object : args) { // 逐一检查方法的参数
            if (object == null || object instanceof MappedStatement) {
                continue;
            } else if (object instanceof MapperMethod.ParamMap<?>) {
                // @see MethodSignature#convertArgsToSqlCommandParam(Object[])
                MapperMethod.ParamMap<?> map = (MapperMethod.ParamMap<?>) object;
                for (String key : map.keySet()) {
                    if (key.startsWith("param")) {
                        dataIsolationControlling.handle(map.get(key), operateType);
                    }
                }
            } else if (object instanceof DefaultSqlSession.StrictMap<?>) {
                // @see DefaultSqlSession#wrapCollection(Object)
                DefaultSqlSession.StrictMap<?> map = (DefaultSqlSession.StrictMap<?>) object;
                for (Object o : map.values()) {
                    if (o instanceof Collection) {
                        for (Object s : (Collection<?>) o) {
                            dataIsolationControlling.handle(s, operateType);
                        }
                    } else if (o.getClass().isArray()) {
                        for (Object s : (Object[]) o) {
                            dataIsolationControlling.handle(s, operateType);
                        }
                    } else if (o != null) {
                        dataIsolationControlling.handle(o, operateType);
                    }
                }
            } else {
                dataIsolationControlling.handle(object, operateType);
            }
        }

        return invocation.proceed();
    }

}
